#pragma once 

#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <unordered_map>
#include <algorithm>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/sendfile.h>
#include <sys/wait.h>
#include "Util.hpp"
#include "Log.hpp"


#define SEP ": "
#define WEB_ROOT "wwwroot"
#define HOME_PAGE "index.html"
#define HTTP_VERSION "HTTP/1.0"
#define LINE_END "\r\n"

#define OK 200
#define NOT_FOUND 404
#define PAGE_404 "404.html"

using std::string;
using std::vector;

static string Code2Dexc(int code)
{
  string line;
  switch(code)
  {
    case 200:
      line="OK";
     break;
    case 404:
     line="NOT FOUND";
       break;
default:
     break;
  }
  return line;
}

static string Suffix2Desc(const string& suffix)
{
  std::unordered_map<string,string>suffix2desc{
    {".html","text/html"},
    {".css","text/css"},
    {".js","application/x-javascript"},
    {".jpg","image/jpeg"},
    {".xml","text/xml"}
  };
  auto iter=suffix2desc.find(suffix);
  if(iter!=suffix2desc.end())
  {
    return iter->second;
  }
  return "text/html";
}

class Httprequest{
  public:
    string request_line;
    vector<string> request_hander;
    string black;
    string request_body;

    //解析完之后的数据
    //请求
    string method;
    string uri;
    string version;
    //环境
    string path;
    //信息
    string query_string;
    //存储hander
    std::unordered_map<string,string> hander_kv;
    //有无正文，正文长度
    int content_length;
    //文件类型后缀
    string suffix;
    //CGI
    bool cgi;
    Httprequest():content_length(0),cgi(false){}
    ~Httprequest(){}
};

class Httpresponse{
  public:
    string response_line;
    vector<string> response_hander;
    string black=LINE_END;
    string response_body;
    //返回的码信息
    int status_code;
    //文件描述符
    int fd=-1;
    //正文大小
    int bodysize=0;
    Httpresponse():status_code(OK){}
    ~Httpresponse(){}
};

class Endpoint{
  private:
    int sock;
    Httprequest http_request;
    Httpresponse http_response;
    bool stop=false;
  private:
    //读取请求行
    bool RecvHttprequestline()
    {
      string& line=http_request.request_line;
      if(Util::Readline(sock,line)<=0)
      {
        stop=true;
        return stop;
      }
      line.resize(line.size()-1);
      //LOG(INFO,line);
      return stop;
    }
    //读取请求报头
    bool RecvHttprequesthander()
    {
      string s;
      while(true)
      {
        s.clear();
        if(Util::Readline(sock,s)<=0)
        {
          stop = true;
          break;
        }
        if(s=="\n"){
          http_request.black=s;
          break;
        }
        s.resize(s.size()-1);
        http_request.request_hander.push_back(s);
        //LOG(INFO,s);
      }
      return stop;
    }
    //解析请求行
    void Rarsehttprequestline()
    {
      string &line=http_request.request_line;
      std::stringstream ss(line);
      ss >> http_request.method >> http_request.uri >> http_request.version;
      //将method转化为全大写防止对方http没有按要求传，保证后面的逻辑正确性
      string& _method=http_request.method;
      std::transform(_method.begin(),_method.end(),_method.begin(),::toupper);
      //std::cout<<http_request.method<<std::endl;
    }
    //解析报头并存入哈希表中方便查找
    void Rarsehttprequesthander()
    {
      string key;
      string value;
      for(auto& iter:http_request.request_hander)
      {
        if(Util::Cutstring(iter,key,value,SEP)){
          http_request.hander_kv[key]=value;
        }
      }
    }
    //判断是否需要读取正文
    bool IsneedRarsehttprequestbody()
     {
       auto &method=http_request.method;
       if(method=="POST")
       {
         auto& hander_kv=http_request.hander_kv;
         auto iter=hander_kv.find("Content-Length");
         if(iter!=hander_kv.end())
         {
           http_request.content_length=atoi(iter->second.c_str());
           return true;
         }
       }
       return false;
     }
    //读取正文
     bool Rarsehttprequestbody()
     {
       if(IsneedRarsehttprequestbody())
       {
          int clength=http_request.content_length;
          auto& body=http_request.request_body;
          char ch=0;
          while(clength){
            ssize_t ret=recv(sock,&ch,1,0);
            if(ret>0){
              body.push_back(ch);
              clength--;
            }
            else {
              stop=true;
              break;
            }
          }
       }
       return stop;
     }
     //处理返回的http
     void Buildhttpresponsehelper()
     {
        auto& code=http_response.status_code;

        http_response.response_line=HTTP_VERSION;
        http_response.response_line+=" ";
        http_response.response_line+=std::to_string(code);
        http_response.response_line+=" ";
        http_response.response_line+=Code2Dexc(code);
        http_response.response_line+=LINE_END;

        string path=WEB_ROOT;
        path+="/";
        switch(code)
        {
          case NOT_FOUND:
            path+=PAGE_404;
            Handlererror(path);
            break;
          case OK:
            HanderOK();
            break;
          default:
            break;
        }

     }
     //正确的处理
     void HanderOK()
     {
       string line="Content-Type: ";
       line+=Suffix2Desc(http_request.suffix);
       line+=LINE_END;
       http_response.response_hander.push_back(line);
      
       line="Content-Length: ";
       if(http_request.cgi)
       {
         line+=std::to_string(http_response.response_body.size());
       }
       else
       {
         line+=std::to_string(http_response.bodysize);
       }
       line+=LINE_END;
       http_response.response_hander.push_back(line);
     }
     //错误时候的处理
     void Handlererror(string page)
     {
       std::cout<<"debug"<<page<<std::endl;
       //防止逻辑错误cgi设为false
       http_request.cgi=false;
       http_response.fd=open(page.c_str(),O_RDONLY);
       if(http_response.fd>0)
       {
         struct stat st;
         stat(page.c_str(),&st);
         http_response.bodysize=st.st_size;
         string line="Content-Type: text/html";
         line+=LINE_END;
         http_response.response_hander.push_back(line);
         line="Content-Length: ";
         line+=std::to_string(st.st_size);
         line+=LINE_END;
         http_response.response_hander.push_back(line);
       }

     }
      //返回静态网页
     int ProcessNonCgi()
     {
       http_response.fd=open(http_request.path.c_str(),O_RDONLY);
       if(http_response.fd>0){
        return OK;
       }
       return NOT_FOUND;
     }
     //CGI请求方法
     int ProcessCgi()
     {
       auto& code=http_response.status_code;
       auto& method=http_request.method;
       auto& quert=http_request.query_string;
       auto& body=http_request.request_body;
       string lenth=std::to_string(http_request.content_length);
       string method_env;
       string quert_env;
       string content_lenth_env;
       //建立2个匿名管道保证双方通信，0是接受，1是输入
       //以父进程的角度命名out 是写入 in是接收
       int input[2];
       int output[2];
       if(pipe(input)<0)
       {
         LOG(ERROR,"input error");
         return NOT_FOUND;
       }
       if(pipe(output)<0){
         LOG(ERROR,"output error");
         return NOT_FOUND;
       }
       auto& bin=http_request.path;
       pid_t pid=fork();
       if(pid==0)
       {
          //child exec*
          close(input[0]);
          close(output[1]);
          //GET方法传入的数据一般会少一些，所以用传环境变量会更快速
          //先传入method告诉子进程改用什么方法读取到数据 GET就是getenv POST就是管道
          method_env="METHOD=";
          method_env+=method;
          putenv((char*)method_env.c_str());
          if(method=="GET")
          {
            quert_env="QUERY_STRING=";
            quert_env+=quert;
            putenv((char*)quert_env.c_str());
            LOG(INFO,"GET");
          }
          else 
          {
            //POST方法
            content_lenth_env="LENGTH=";
            content_lenth_env+=lenth;
            putenv((char*)content_lenth_env.c_str());
            LOG(INFO,"POST");
          }
          //程序替换会导致新程序不知道曾经打开的文件描述符，所以在替换之前将文件描述符重定向到默认的输入输出
          dup2(input[1],1);
          dup2(output[0],0);
          execl(bin.c_str(),bin.c_str(),nullptr);
          LOG(WARING,"execl err");
          exit(1);
       }
       else if(pid<0)
       {
         LOG(ERROR,"fock error");
         return NOT_FOUND;
       }
       else{
         close(input[1]);
         close(output[0]);
         //如果是POST方法就从管道里面写入
         //应为POST的size是不确定大小的所以用管道
         if(method=="POST")
         {
           const char* start=body.c_str();
           size_t target=0;
           ssize_t size=0;
           while((target<http_request.request_body.size())&&(size=write(output[1],start+target,body.size()-target))>0)
           {
             target+=size;
           }
         }
         int status=0;
         int ret=waitpid(pid,&status,0);
         if(ret==pid)
         {
           char ch=0;
           if(WIFEXITED(status))
           {
             if(WEXITSTATUS(status)==0)
             {
               //正常退出,写端关闭，读端会读到0
               while (read(input[0], &ch, 1))
               {
                 //子进程会自动退出,读上来的数据放进response_body当中
                 http_response.response_body.push_back(ch);
                 //std::cerr << "debug father read success: " << ch << std::endl;
               }
               //response_body此时才会被设置
               //std::cerr << "debug :" << http_response.response_body << std::endl;
               code = OK;
             }
             else{
               code=NOT_FOUND;
             }
           }
           else{
             code=NOT_FOUND;
           }
         }

         close(input[0]);
         close(output[1]);
       }
       return code;
     }
  public:
    Endpoint(int _sock):sock(_sock)
    {}
    bool IsStop()
    {
      return stop;
    }
    void Recvhttprequest()
    {
      //获得请求
      if(!(RecvHttprequestline())&&!(RecvHttprequesthander()))
      {
        //解析请求
        Rarsehttprequestline();
        //解析报头
        Rarsehttprequesthander();
        //是否获得正文
        Rarsehttprequestbody();
      }
    }
    //构建相应
    void Buildhttprequest()
    {
      string _path;
      int size=0;
      size_t _pos;
      auto& code=http_response.status_code;
      if(http_request.method!="GET"&&http_request.method!="POST")
      {
        //非法请求
        LOG(WARNING,"method is not right");
        code=NOT_FOUND;
        goto END;
      }
      if(http_request.method=="GET")
      {
        //解析uri 
        size_t pos=http_request.uri.find('?');
        if(pos!=string::npos){
          Util::Cutstring(http_request.uri,http_request.path,http_request.query_string,"?");
          //设置CGI
          http_request.cgi=true;
        }
        else{
          http_request.path=http_request.uri;
        }
      }
      else if(http_request.method=="POST")
      {
          //post方法
          http_request.path=http_request.uri;
          http_request.cgi=true;
      }
      else
      {
        //DO NOTHING
      }
      //对uri进行加工
      _path=http_request.path;
      http_request.path=WEB_ROOT;
      http_request.path+=_path;
      if(http_request.path[http_request.path.size()-1]=='/')
      {
        http_request.path+=HOME_PAGE;
      }
      //查看目录是否存在 st是权限
      struct stat st;
      if(stat(http_request.path.c_str(),&st)==0)
      {
        //该目录存在
        //判断是不是根目录
        if(S_ISDIR(st.st_mode))
        {
          //该目录是根目录
          http_request.path+='/';
          http_request.path+=HOME_PAGE;
          stat(http_request.path.c_str(),&st);
        }
        //还要判断是不是可执行程序
        if((st.st_mode&S_IXUSR)||(st.st_mode&S_IXGRP)||(st.st_mode&S_IXOTH))
        {
          //进行特殊处理
          http_request.cgi=true;
        }
        size=st.st_size;
        http_response.bodysize=size;
      }
      else {
        //该目录不存在 返回错误信息
        string line=http_request.path;
        line+=" NOTFOUND";
        LOG(WARNING,line);
        code=NOT_FOUND;
        goto END;
      }
      //提取后缀
      _pos=http_request.path.rfind('.');
      if(_pos==string::npos)
      {
        http_request.suffix=".html";
      }
      else{
        http_request.suffix=http_request.path.substr(_pos);
      }
      code=200;
      //方法正确，请求也正确，就应该处理方法了
      if(http_request.cgi)
      {
        code=ProcessCgi();
      }
      else
      {
        code=ProcessNonCgi();//简单的网页返回，返回静态网页
      }
END:
      Buildhttpresponsehelper();
    }

    void Sendhttprequest()
    {
      send(sock,http_response.response_line.c_str(),http_response.response_line.size(),0);
      for(auto iter:http_response.response_hander){
        send(sock,iter.c_str(),iter.size(),0);
      }
      send(sock,http_response.black.c_str(),http_response.black.size(),0);
      if(http_request.cgi)
      {
        size_t total=0;
        const char* start=http_response.response_body.c_str();
        string& body=http_response.response_body;
        ssize_t size=0;
        while((total<body.size())&&(size=write(sock,start+total,body.size()-total))>0)
        {
          total+=size;
        }
      }
      else
      {
        sendfile(sock,http_response.fd,nullptr,http_response.bodysize);
        close(http_response.fd);
      }
    }

    ~Endpoint()
    {
      close(sock);
    }
};

class CallBack{
  public:
    void operator()(int *sock)
    {
      HandlerRequest(sock);
    }
    void HandlerRequest(int* _sock)
    {
      LOG(INFO,"Hander Request Begin");
      int sock=*_sock;
      //if(1){
            //用于测试浏览器发送的数据
        //char str2[4096];
        //recv(sock, str2, sizeof(str2) - 1, 0);
        //std::cout << "--------------------------" << std::endl;
        //std::cout << str2 << std::endl;
        //std::cout << "--------------------------" << std::endl;
        //}
      //else{
      Endpoint* ep=new Endpoint(sock);
      ep->Recvhttprequest();
      if(!ep->IsStop())
      {
        LOG(INFO, "Recv no error,begin Build and Send");
        ep->Buildhttprequest();
        ep->Sendhttprequest();
      }
			else
			{
				LOG(INFO, "Recv error,stop Build and Send");
			}
      delete ep;
      //}
      LOG(INFO,"Hanher Requesy End");
    }
};


